/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002-2006 * Sleepycat Software. All rights reserved. * * $Id: PackedOffsets.java,v 1.1 2006/05/06 09:01:57 ckaestne Exp $ */ package com.sleepycat.je.cleaner; import java.nio.ByteBuffer; import java.util.Arrays; import com.sleepycat.je.log.LogReadable; import com.sleepycat.je.log.LogUtils; import com.sleepycat.je.log.LogWritable; /** * Stores a sorted list of LSN offsets in a packed short representation. Each * stored value is the difference between two consecutive offsets. The stored * values are stored as one or more shorts where each short holds 0x7fff * values. Shorts are in LSB order. The value is negated if more shorts for * the same offset follow; this works because offsets are always positive * values. */ public class PackedOffsets implements LogWritable, LogReadable { private short[] data; private int size; /** * Creates an empty object. */ public PackedOffsets() { } /** * Returns an iterator over all offsets. */ Iterator iterator() { return new Iterator(); } /** * Packs the given offsets, replacing any offsets stored in this object. */ public void pack(long[] offsets) { /* Allocate a maximum sized new data array. */ short[] newData = new short[offsets.length * 3]; /* Pack the sorted offsets. */ Arrays.sort(offsets); int dataIndex = 0; long priorVal = 0; for (int i = 0; i < offsets.length; i += 1) { long val = offsets[i]; dataIndex = append(newData, dataIndex, val - priorVal); priorVal = val; } /* Copy in the exact sized new data. */ data = new short[dataIndex]; System.arraycopy(newData, 0, data, 0, dataIndex); size = offsets.length; } /** * Returns the unpacked offsets. */ long[] toArray() { long[] offsets = new long[size]; int index = 0; Iterator iter = iterator(); while (iter.hasNext()) { offsets[index++] = iter.next(); } assert index == size; return offsets; } /** * Copies the given value as a packed long to the array starting at the * given index. Returns the index of the next position in the array. */ private int append(short[] to, int index, long val) { assert val >= 0; while (true) { short s = (short) (val & 0x7fff); val >>>= 15; if (val > 0) { to[index++] = (short) (-1 - s); } else { to[index++] = s; break; } } return index; } /** * An iterator over all offsets. */ class Iterator { private int index; private long priorVal; private Iterator() { } boolean hasNext() { return data != null && index < data.length; } long next() { long val = priorVal; for (int shift = 0;; shift += 15) { long s = data[index++]; if (s < 0) { val += (-1 - s) << shift; } else { val += s << shift; break; } } priorVal = val; return val; } } /** * @see LogWritable#getLogSize */ public int getLogSize() { return (2 * LogUtils.getIntLogSize()) + ((data != null) ? (data.length * LogUtils.SHORT_BYTES) : 0); } /** * @see LogWritable#writeToLog */ public void writeToLog(ByteBuffer buf) { LogUtils.writeInt(buf, size); if (data != null) { LogUtils.writeInt(buf, data.length); for (int i = 0; i < data.length; i += 1) { LogUtils.writeShort(buf, data[i]); } } else { LogUtils.writeInt(buf, 0); } } /** * @see LogReadable#readFromLog */ public void readFromLog(ByteBuffer buf, byte entryTypeVersion) { size = LogUtils.readInt(buf); int len = LogUtils.readInt(buf); if (len > 0) { data = new short[len]; for (int i = 0; i < len; i += 1) { data[i] = LogUtils.readShort(buf); } } } /** * @see LogReadable#dumpLog */ public void dumpLog(StringBuffer buf, boolean verbose) { if (size > 0) { Iterator i = iterator(); buf.append("<offsets size=\""); buf.append(size); buf.append("\">"); while (i.hasNext()) { buf.append("0x"); buf.append(Long.toHexString(i.next())); buf.append(' '); } buf.append("</offsets>"); } else { buf.append("<offsets size=\"0\"/>"); } } /** * Never called. * @see LogReadable#getTransactionId */ public long getTransactionId() { return -1; } /** * Never called. * @see LogReadable#logEntryIsTransactional */ public boolean logEntryIsTransactional() { return false; } public String toString() { StringBuffer buf = new StringBuffer(); dumpLog(buf, true); return buf.toString(); } }